package com.easydata.libPlugin.core;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.easydata.libPlugin.core.consts.MessageType;
import com.easydata.libPlugin.core.interfaces.IDAGNode;
import com.easydata.libPlugin.core.interfaces.IDataWriter;
import lombok.Getter;
import lombok.Setter;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public abstract class AbstractNode implements IDAGNode {
    @Getter
    @Setter
    private String id;

    protected HashSet<AbstractEventListener> eventListeners = new HashSet<>();

    @Getter
    @Setter
    private AbstractContext context;


    public void registerEventListener(AbstractEventListener listener) {
        eventListeners.add(listener);
    }

    public void removeEventListener(AbstractEventListener listener) {
        eventListeners.remove(listener);
    }

    protected synchronized void log(String fmt, Object... args) {
        if (this.context == null) {
            return;
        }

        String innerFmt = "[Node " + this.id + "][" + this.getClass().getName() + "]" + fmt;
        this.context.log(innerFmt, args);
    }

    protected void prepare(JSONObject parameter) throws Exception {
    }

    protected void start() throws Exception {
    }

    protected void process(JSONArray data) throws Exception {
    }

    protected void done() {

    }

    protected void stop() throws Exception {

    }

    protected Object onMessage(Message msg) {
        return null;
    }

    protected Response onRequest(Request req) {
        return Response.builder().code(-1).msg("command not found").build();
    }

    final protected void exception(Exception ex) {
        eventListeners.stream().filter(Objects::nonNull).forEach(evt -> {
            evt.onException(this, ex);
        });
    }

    public String ui() {
        InputStream resourceAsStream = this.getClass().getResourceAsStream("/ui.json");

        if (resourceAsStream == null) {
            return "[]";
        }

        try {
            byte[] bytes = resourceAsStream.readAllBytes();
            return new String(bytes);
        } catch (IOException e) {
            return "[]";
        }
    }

    @Override
    final public Object post(Message msg) {
        if (msg == null) return null;

        eventListeners.stream().filter(Objects::nonNull).forEach(evt -> {
            evt.onMessage(this, msg);
        });

        switch (msg.getType()) {
            case MessageType.PREPARE -> {
                try {
                    this.prepare((JSONObject) msg.getData());
                    eventListeners.stream().filter(Objects::nonNull).forEach(evt -> {
                        evt.onPrepared(msg.getNode(), (JSONObject) msg.getData());
                    });

                } catch (Exception e) {
                    this.exception(e);
                }

                return null;
            }
            case MessageType.START -> {
                try {
                    this.start();
                    eventListeners.stream().filter(Objects::nonNull).forEach(evt -> {
                        evt.onStarted(msg.getNode());
                    });

                } catch (Exception e) {
                    this.exception(e);
                }



                return null;
            }
            case MessageType.PROCESS -> {
                try {
                    this.process((JSONArray) msg.getData());
                    eventListeners.stream().filter(Objects::nonNull).forEach(evt -> {
                        evt.onProcessed(msg.getNode(), (JSONArray) msg.getData());
                    });
                } catch (Exception e) {
                    this.exception(e);
                }



                return null;
            }
            case MessageType.DONE -> {
                try {
                    this.done();

                    eventListeners.stream().filter(Objects::nonNull).forEach(evt -> {
                        evt.onDone(msg.getNode());
                    });

                } catch (Exception e) {
                    this.exception(e);
                }



                return null;
            }
            case MessageType.STOP -> {
                try {
                    this.stop();
                } catch (Exception e) {
                    this.exception(e);
                }

                eventListeners.stream().filter(Objects::nonNull).forEach(evt -> {
                    evt.onStopped(msg.getNode());
                });

                return null;
            }
            case MessageType.REQUEST -> {
                Request req = (Request) msg.getData();

                if (req == null) {
                    return null;
                }

                return this.onRequest(req);
            }
        }

        return this.onMessage(msg);
    }

}
